import json
import webbrowser
from datetime import datetime
from flask import current_app

from flask_restx import Namespace, Resource, fields, Model
# from pycamunda import BadRequest
from werkzeug.exceptions import BadRequest, NotFound
from pycamunda.externaltask import FetchAndLock, Complete
from pycamunda.variable import Variable  # 修改导入路径
import logging
import requests
from flask import request
from marshmallow import validate, Schema, fields as marshmallow_fields
from functools import wraps
from app1 import cache

# 定义 API 命名空间
ns_cmn = Namespace('ns_cmn', description='External Task Operations')

CAMUNDA_URL = "http://192.168.6.137:7789/engine-rest"
WORKER_ID = "flask_worker"
TOPIC_NAME = "charge-card"

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 定义数据模型
task_config_model = ns_cmn.model('TaskConfig', {
    'camunda_url': fields.String(
        required=False,
        default="http://192.168.6.137:7789/engine-rest",
        description='Camunda引擎REST地址'
    ),
    'worker_id': fields.String(
        required=False,
        default="flask_worker",
        description='工作者ID'
    ),
    'topic_name': fields.String(
        required=False,
        default="charge-card",
        description='监听的主题名称'
    ),
    'variables': fields.List(
        fields.String,
        required=False,
        default=["item", "amount"],
        description='需要获取的变量名列表'
    )
})

task_response_model = ns_cmn.model('TaskResponse', {
    'status': fields.String,
    'task_id': fields.String,
    'message': fields.String(required=False)
})

error_model = ns_cmn.model('Error', {
    'error': fields.String,
    'details': fields.String(required=False)
})


@ns_cmn.route('/external-task/process')
class ExternalTaskProcessor(Resource):
    @ns_cmn.expect(task_config_model)
    @ns_cmn.response(200, 'Success', task_response_model)
    @ns_cmn.response(400, 'Invalid request', error_model)
    @ns_cmn.response(500, 'Error', error_model)
    def post(self):
        """
        处理外部任务（可配置参数）
        - camunda_url: Camunda引擎地址
        - worker_id: 工作者ID
        - topic_name: 监听主题
        - variables: 需要获取的变量列表
        """
        try:
            # 1. 获取输入参数
            config = request.get_json() or {}
            camunda_url = config.get('camunda_url', "http://192.168.6.137:7789/engine-rest")
            worker_id = config.get('worker_id', "flask_worker")
            topic_name = config.get('topic_name', "charge-card")
            variables = config.get('variables', ["item", "amount"])

            # 2. 验证参数
            if not all([camunda_url.startswith('http'), worker_id, topic_name]):
                raise BadRequest("Invalid configuration parameters")

            # 3. 初始化任务获取器
            fetch = FetchAndLock(
                url=camunda_url,
                worker_id=worker_id,
                max_tasks=1
            )

            # 4. 添加任务主题配置
            fetch.add_topic(
                name=topic_name,
                lock_duration=10000,  # 锁定10秒
                variables=variables
            )

            # 5. 获取并锁定任务
            tasks = fetch()
            if not tasks:
                return {"status": "skipped", "message": "No tasks available"}, 200

            task = tasks[0]
            logger.info(f"Processing task {task.id_} with worker {worker_id}")

            # 6. 获取流程变量
            task_vars = task.variables
            try:
                item = task_vars["item"].value
                amount = task_vars["amount"].value
                logger.info(f"Charging {amount}€ for {item}")
            except KeyError as e:
                logger.warning(f"Missing variable in task: {str(e)}")

            # 7. 模拟业务操作
            webbrowser.open("https://docs.camunda.org")

            # 8. 完成任务
            Complete(
                url=camunda_url,
                id_=task.id_,
                worker_id=worker_id
            )()

            return {
                "status": "completed",
                "task_id": task.id_,
                "worker_id": worker_id,
                "topic": topic_name
            }, 200

        except BadRequest as e:
            logger.error(f"Bad request: {str(e)}")
            return {"error": str(e)}, 400
        except requests.exceptions.RequestException as e:
            err_msg = f"Camunda connection failed: {str(e)}"
            logger.error(err_msg)
            return {"error": err_msg}, 503
        except Exception as e:
            err_msg = f"Processing error: {str(e)}"
            logger.error(err_msg, exc_info=True)
            return {"error": err_msg}, 500


# task 查询
# 1. 定义数据模型
task_type_model = ns_cmn.model('TaskType', {
    'task_type': fields.String(
        required=False,
        description='任务类型(user/external/history/case)，空值则搜索所有类型',
        enum=['user', 'external', 'history', 'case'],
        example="user"
    ),
    'task_id': fields.String(
        required=True,
        description='任务ID',
        example="c2f5f792-6486-11f0-8678-0aecfb32578a"
    )
})

task_model = ns_cmn.model('Task', {
    'id': fields.String(description='任务ID'),
    'type': fields.String(description='任务类型(user/external/history/case)'),
    'data': fields.Raw(description='任务详情数据')
})


# 2. 实现通用任务查询
@ns_cmn.route('/task')
class UniversalTaskQuery(Resource):

    @ns_cmn.doc('通用任务查询')
    @ns_cmn.expect(task_type_model)
    @ns_cmn.response(200, '成功', fields.List(fields.Nested(task_model)))
    @ns_cmn.response(400, '参数错误')
    @ns_cmn.response(404, '任务不存在')
    def post(self):
        """
        通过ID查询任务（支持所有类型）
        - 指定task_type时精确查询该类型
        - 不指定task_type时尝试所有类型
        """
        try:
            data = request.get_json()
            task_id = data.get('task_id')
            task_type = data.get('task_type', '').lower()

            if not task_id:
                raise BadRequest("task_id不能为空")

            # ID格式验证
            if not self._is_valid_id(task_id):
                raise BadRequest("非法任务ID格式")

            # 定义类型查询顺序（无指定类型时）
            search_order = []
            if task_type:
                search_order = [task_type]
            else:
                search_order = ['user', 'external', 'history', 'case']

            # 按顺序尝试查询
            results = []
            for t_type in search_order:
                task_data = self._query_camunda(task_id, t_type)
                if task_data:
                    results.append({
                        "type": t_type,
                        "data": task_data
                    })
                    # 如果指定了类型，找到第一个就返回
                    if task_type:
                        break

            if not results:
                raise NotFound("任务不存在")

            return results, 200

        except BadRequest as e:
            ns_cmn.abort(400, str(e))
        except NotFound as e:
            ns_cmn.abort(404, str(e))
        except Exception as e:
            current_app.logger.error(f"查询任务失败: {str(e)}")
            ns_cmn.abort(500, "服务器内部错误")

    def _query_camunda(self, task_id, task_type):
        """根据类型查询Camunda"""
        endpoints = {
            'user': '/task/',
            'external': '/external-task/',
            'history': '/history/task/',
            'case': '/case-execution/'
        }

        try:
            response = requests.get(
                f"{current_app.config['CAMUNDA_ENGINE_URL']}{endpoints[task_type]}{task_id}",
                timeout=3
            )
            if response.status_code == 200:
                return response.json()
            return None
        except:
            return None

    def _is_valid_id(self, task_id):
        """ID格式验证"""
        return len(task_id) >= 10  # 基础长度校验


# 1. 历史任务完整响应模型
history_task_full_model = ns_cmn.model('HistoryTaskFull', {
    'id': fields.String(description='任务ID'),
    'name': fields.String(description='任务名称'),
    'assignee': fields.String(description='处理人'),
    'processInstanceId': fields.String(description='流程实例ID'),
    'processDefinitionId': fields.String(description='流程定义ID'),
    'processDefinitionKey': fields.String(description='流程定义Key'),
    'taskDefinitionKey': fields.String(description='任务定义Key'),
    'startTime': fields.DateTime(description='开始时间'),
    'endTime': fields.DateTime(description='结束时间'),
    'durationInMillis': fields.Integer(description='持续时间(毫秒)'),
    'deleteReason': fields.String(description='删除原因'),
    'priority': fields.Integer(description='优先级'),
    'tenantId': fields.String(description='租户ID'),
    'removalTime': fields.DateTime(description='数据清除时间')
})

# 2. 请求体模型（明确空字符串行为）
history_task_query_model = ns_cmn.model('HistoryTaskQueryParams', {
    'processDefinitionKey': fields.String(
        required=False,
        description='空字符串""=不过滤，不传=不过滤',
        example="Process_1jrmxqa"
    ),
    'finished': fields.String(
        required=False,
        description='空字符串""=不过滤，"true"/"false"=过滤，不传=不过滤',
        enum=['', 'true', 'false'],
        example="true"
    ),
    'taskName': fields.String(
        required=False,
        description='空字符串""=不过滤，不传=不过滤',
        example="审批"
    ),
    'assignee': fields.String(
        required=False,
        description='空字符串""=不过滤，不传=不过滤',
        example="user1"
    ),
    'dueAfter': fields.String(
        required=False,
        description='空字符串""=不过滤，ISO格式时间=过滤，不传=不过滤',
        example="2025-07-01T00:00:00+08:00"
    ),
    'dueBefore': fields.String(
        required=False,
        description='空字符串""=不过滤，ISO格式时间=过滤，不传=不过滤',
        example="2025-07-31T23:59:59+08:00"
    ),
    'sortBy': fields.String(
        required=False,
        description='空字符串""=默认排序，不传=默认排序',
        enum=['', 'endTime', 'startTime', 'duration'],
        example="endTime"
    ),
    'sortOrder': fields.String(
        required=False,
        description='空字符串""=默认顺序，不传=默认顺序',
        enum=['', 'asc', 'desc'],
        example="desc"
    )
})


# 3. 接口实现
@ns_cmn.route('/history-tasks')
class HistoryTaskQueryPost(Resource):
    @ns_cmn.doc('历史任务查询(POST)')
    @ns_cmn.expect(history_task_query_model)
    @ns_cmn.response(200, '成功', fields.List(fields.Nested(history_task_full_model)))
    def post(self):
        """
        历史任务查询规则：
        - 字符串参数传空字符串""或不传：都表示不过滤该条件
        - finished: ""或不传=包含全部, "true"/"false"=精确过滤
        - 时间参数: ""或不传=不过滤，有效ISO时间=过滤
        """
        try:
            # 接收参数（空body视为全空参数）
            json_data = request.get_json() or {
                'processDefinitionKey': '',
                'finished': '',
                'taskName': '',
                'assignee': '',
                'dueAfter': '',
                'dueBefore': '',
                'sortBy': '',
                'sortOrder': ''
            }

            # 参数校验Schema
            class QuerySchema(Schema):
                processDefinitionKey = marshmallow_fields.String(required=False)
                finished = marshmallow_fields.String(
                    required=False,
                    validate=validate.OneOf(['', 'true', 'false'])
                )
                taskName = marshmallow_fields.String(required=False)
                assignee = marshmallow_fields.String(required=False)
                dueAfter = marshmallow_fields.String(required=False)
                dueBefore = marshmallow_fields.String(required=False)
                sortBy = marshmallow_fields.String(
                    required=False,
                    validate=validate.OneOf(['', 'endTime', 'startTime', 'duration'])
                )
                sortOrder = marshmallow_fields.String(
                    required=False,
                    validate=validate.OneOf(['', 'asc', 'desc'])
                )

            args = QuerySchema().load(json_data)

            # 构建Camunda查询参数（空字符串不加入查询条件）
            camunda_params = {}

            # 处理各个参数（只有非空字符串才加入查询）
            if args.get('processDefinitionKey', ''):
                camunda_params['processDefinitionKey'] = args['processDefinitionKey']
            if args.get('finished', '') in ['true', 'false']:
                camunda_params['finished'] = args['finished']
            if args.get('taskName', ''):
                camunda_params['taskName'] = args['taskName']
            if args.get('assignee', ''):
                camunda_params['assignee'] = args['assignee']

            # 处理时间参数
            if args.get('dueAfter', ''):
                try:
                    datetime.fromisoformat(args['dueAfter'])  # 验证时间格式
                    camunda_params['dueAfter'] = args['dueAfter']
                except ValueError:
                    raise BadRequest("dueAfter必须是有效的ISO时间格式")

            if args.get('dueBefore', ''):
                try:
                    datetime.fromisoformat(args['dueBefore'])
                    camunda_params['dueBefore'] = args['dueBefore']
                except ValueError:
                    raise BadRequest("dueBefore必须是有效的ISO时间格式")

            # 处理排序
            if args.get('sortBy', ''):
                camunda_params['sortBy'] = args['sortBy']
            if args.get('sortOrder', ''):
                camunda_params['sortOrder'] = args['sortOrder']

            # 调用Camunda API
            response = requests.get(
                f"{CAMUNDA_URL}/history/task",
                params=camunda_params if camunda_params else None,  # 无参数时不传params
                timeout=10
            )
            response.raise_for_status()

            return response.json(), 200

        except BadRequest as e:
            return {'message': str(e)}, 400
        except requests.exceptions.RequestException as e:
            return {'message': f"Camunda接口错误: {str(e)}"}, 502
        except Exception as e:
            return {'message': f"服务器内部错误: {str(e)}"}, 500


# 可选：添加变量查询接口
@ns_cmn.route('/tasks/<string:task_id>/variables')
class HistoryTaskVariables(Resource):
    @ns_cmn.doc('查询历史任务变量')
    @ns_cmn.response(200, '成功', model=fields.Raw(description='变量键值对'))
    @ns_cmn.response(500, '服务器错误')
    def get(self, task_id):
        """获取指定历史任务的变量"""
        try:
            # 调用 Camunda API 查询变量
            response = requests.get(
                f"{CAMUNDA_URL}/history/variable-instance",
                params={'taskIdIn': task_id}
            )
            response.raise_for_status()

            # 直接返回原始 JSON 数据（不进行序列化）
            variables = response.json()

            # 转换为更易读的键值对格式（可选）
            variable_dict = {var['name']: var['value'] for var in variables}

            return variable_dict, 200
        except requests.exceptions.RequestException as e:
            ns_cmn.abort(502, f"Camunda 引擎连接失败: {str(e)}")
        except Exception as e:
            ns_cmn.abort(500, f"变量查询失败: {str(e)}")


@ns_cmn.route('/process-instances/<string:process_instance_id>/variables')
class ProcessInstanceVariables(Resource):
    @ns_cmn.doc('查询流程实例变量')
    @ns_cmn.response(200, '成功', model=fields.Raw(description='变量键值对'))
    @ns_cmn.response(404, '流程实例不存在')
    @ns_cmn.response(500, '服务器错误')
    def get(self, process_instance_id):
        """获取指定流程实例的全局变量"""
        try:
            # 调用 Camunda API
            response = requests.get(
                f"{CAMUNDA_URL}/process-instance/{process_instance_id}/variables"
            )

            # 处理404情况
            if response.status_code == 404:
                ns_cmn.abort(404, f"流程实例 {process_instance_id} 不存在")

            response.raise_for_status()

            # 转换变量格式（Camunda返回格式为 {"var1": {"value": "xxx", "type": "String"}, ...}）
            variables = {
                name: data["value"]
                for name, data in response.json().items()
            }

            return variables, 200

        except requests.exceptions.RequestException as e:
            ns_cmn.abort(502, f"Camunda引擎连接失败: {str(e)}")
        except Exception as e:
            ns_cmn.abort(500, f"变量查询失败: {str(e)}")


@ns_cmn.route('/variables')
class CombinedVariables(Resource):
    @ns_cmn.doc('通用变量查询（支持Task或流程实例）')
    @ns_cmn.param('task_id', '任务ID（User Task用）', _in='query')
    @ns_cmn.param('process_instance_id', '流程实例ID（Service Task用）', _in='query')
    def get(self):
        """合并查询（同时支持两种任务）"""
        task_id = request.args.get('task_id')
        process_instance_id = request.args.get('process_instance_id')

        if task_id:
            params = {'taskIdIn': task_id}
        elif process_instance_id:
            params = {'processInstanceIdIn': process_instance_id}
        else:
            ns_cmn.abort(400, "必须提供task_id或process_instance_id")

        response = requests.get(f"{CAMUNDA_URL}/history/variable-instance", params=params)
        return {var['name']: var['value'] for var in response.json()}, 200


# 数据模型
variable_model = ns_cmn.model('VariableInstance', {
    'id': fields.String(description='变量实例ID'),
    'name': fields.String(required=True, description='变量名'),
    'value': fields.Raw(description='变量值'),
    'type': fields.String(description='变量类型'),
    'processInstanceId': fields.String(description='关联的流程实例ID'),
    'executionId': fields.String(description='关联的执行ID'),
    'taskId': fields.String(description='关联的任务ID（如果是任务变量）'),
    'activityInstanceId': fields.String(description='活动实例ID'),
    'tenantId': fields.String(description='租户ID'),
    'errorMessage': fields.String(description='错误信息（如果有）')
})


@ns_cmn.route('/variables')
class HistoryVariableInstance(Resource):
    @ns_cmn.doc('查询历史变量记录',
                params={
                    'process_instance_id': '流程实例ID（下划线风格）',
                    'processInstanceId': '流程实例ID（驼峰风格，兼容旧版）',
                    'task_id': '任务ID（下划线风格）',
                    'taskId': '任务ID（驼峰风格，兼容旧版）',
                    'variable_name': '变量名模糊查询',
                    'sort_by': '排序字段（createTime/name）',
                    'sort_order': '排序方式（asc/desc）'
                })
    @ns_cmn.response(200, '成功', [variable_model])
    @ns_cmn.response(400, '参数错误')
    @ns_cmn.response(404, '未找到记录')
    @ns_cmn.response(500, '服务器错误')
    def get(self):
        """
        查询历史变量记录（兼容两种参数命名风格）
        典型用法：
        GET /variables?process_instance_id=123
        GET /variables?processInstanceId=123&variable_name=order
        """
        try:
            args = request.args
            logger.info(f"Received request with args: {dict(args)}")

            # 参数兼容处理（同时支持驼峰和下划线命名）
            process_instance_id = args.get('process_instance_id') or args.get('processInstanceId')
            task_id = args.get('task_id') or args.get('taskId')
            variable_name = args.get('variable_name') or args.get('variableName')
            sort_by = args.get('sort_by') or args.get('sortBy')
            sort_order = args.get('sort_order') or args.get('sortOrder')

            # 参数验证
            if not process_instance_id and not task_id:
                logger.warning("Missing required parameters")
                ns_cmn.abort(400, "必须提供 process_instance_id 或 task_id 参数")

            # 构建Camunda查询参数（使用官方驼峰式命名）
            camunda_params = {
                'processInstanceId': process_instance_id,
                'taskId': task_id,
                'variableNameLike': f"%{variable_name}%" if variable_name else None,
                'sortBy': sort_by,
                'sortOrder': sort_order
            }
            # 移除空值参数
            camunda_params = {k: v for k, v in camunda_params.items() if v is not None}

            logger.info(f"Querying Camunda with params: {camunda_params}")

            # 调用Camunda引擎API
            response = requests.get(
                f"{CAMUNDA_URL}/history/variable-instance",
                params=camunda_params,
                timeout=10  # 10秒超时
            )

            # 处理响应
            if response.status_code == 404:
                logger.warning(f"No variables found for query: {camunda_params}")
                ns_cmn.abort(404, "未找到匹配的变量记录")

            response.raise_for_status()

            data = response.json()
            logger.info(f"Found {len(data)} variable instances")
            return data, 200

        except requests.exceptions.Timeout:
            logger.error("Camunda API request timeout")
            ns_cmn.abort(504, "Camunda引擎响应超时")
        except requests.exceptions.ConnectionError:
            logger.error("Cannot connect to Camunda engine")
            ns_cmn.abort(502, "无法连接Camunda引擎")
        except requests.exceptions.HTTPError as e:
            logger.error(f"Camunda API error: {str(e)}")
            ns_cmn.abort(502, f"Camunda引擎错误: {e.response.text}")
        except Exception as e:
            logger.exception("Unexpected error occurred")
            ns_cmn.abort(500, f"服务器内部错误: {str(e)}")


variablebyname_model = ns_cmn.model('HistoryVariableByname', {
    'id': fields.String(description='变量实例ID'),
    'name': fields.String(description='变量名'),
    'type': fields.String(description='变量类型'),
    'value': fields.Raw(description='变量值'),
    'processInstanceId': fields.String(description='所属流程实例'),
    'executionId': fields.String(description='执行ID'),
    'taskId': fields.String(description='任务ID'),
    'activityInstanceId': fields.String(description='活动实例ID'),
    'tenantId': fields.String(description='租户ID')
})


# By name
@ns_cmn.route('/variables-by-name')
class HistoryVariablesByName(Resource):
    @ns_cmn.doc('按变量名查询历史变量')
    @ns_cmn.param('name', '变量名（必填）', required=True)
    @ns_cmn.param('maxResults', '最大返回数量（默认50）')
    @ns_cmn.param('firstResult', '起始偏移（默认0）')
    @ns_cmn.marshal_list_with(variablebyname_model)
    def get(self):
        """
        按变量名查询 Camunda 历史变量（支持分页）
        """
        name = request.args.get('name')
        max_results = int(request.args.get('maxResults', 50))
        first_result = int(request.args.get('firstResult', 0))

        if not name:
            ns_cmn.abort(400, "缺少变量名参数 ?name=xxx")

        try:
            params = {
                'variableName': name,
                'maxResults': max_results,
                'firstResult': first_result
            }

            response = requests.get(
                f"{CAMUNDA_URL}/history/variable-instance",
                params=params,
                timeout=5
            )
            response.raise_for_status()

            data = response.json()
            if not data:
                ns_cmn.abort(404, "未找到匹配的变量")

            return data, 200

        except requests.exceptions.RequestException as e:
            ns_cmn.abort(502, f"Camunda请求失败: {str(e)}")
        except Exception as e:
            ns_cmn.abort(500, f"服务器错误: {str(e)}")


# by like name
@ns_cmn.route('/variables-by-likename')
class HistoryVariablesByLikeName(Resource):
    @ns_cmn.doc('按变量名或模糊匹配查询历史变量')
    @ns_cmn.param('name', '变量名（精确匹配）', required=False)
    @ns_cmn.param('nameLike', '变量名模糊匹配（支持%, _）', required=False)
    @ns_cmn.param('maxResults', '最大返回数量（默认50）')
    @ns_cmn.param('firstResult', '起始偏移（默认0）')
    @ns_cmn.marshal_list_with(variablebyname_model)
    def get(self):
        """
        支持通过 name 或 nameLike 查询历史变量（Camunda），并支持分页
        nameLike 支持 SQL 风格的通配符：
        % 表示任意长度字符串
        _ 表示一个字符
        """
        name = request.args.get('name')
        name_like = request.args.get('nameLike')
        max_results = int(request.args.get('maxResults', 50))
        first_result = int(request.args.get('firstResult', 0))

        if not name and not name_like:
            ns_cmn.abort(400, "请提供 name 或 nameLike 查询参数")

        if name and name_like:
            ns_cmn.abort(400, "只能使用 name 或 nameLike 其中之一")

        try:
            params = {
                'maxResults': max_results,
                'firstResult': first_result
            }

            if name:
                params['variableName'] = name
            else:
                params['variableNameLike'] = name_like

            response = requests.get(
                f"{CAMUNDA_URL}/history/variable-instance",
                params=params,
                timeout=5
            )
            response.raise_for_status()

            data = response.json()
            if not data:
                ns_cmn.abort(404, "未找到匹配的变量")

            return data, 200

        except requests.exceptions.RequestException as e:
            ns_cmn.abort(502, f"Camunda请求失败: {str(e)}")
        except Exception as e:
            ns_cmn.abort(500, f"服务器错误: {str(e)}")


# 定义基础参数模型
service_task_query_params = Model('ServiceTaskQueryParams', {
    'activityId': fields.String(
        description='Service Task的BPMN节点ID',
        example='Activity_0u0z4q3',
        required=False
    ),
    'processDefinitionKey': fields.String(
        description='流程定义Key',
        example='Process_1jrmxqa',
        required=False
    ),
    'processInstanceId': fields.String(
        description='流程实例ID',
        example='0353078e-6466-11f0-8678-0aecfb32578a',
        required=False
    ),
    'completed': fields.String(
        description='是否完成（true/false）',
        enum=['true', 'false'],
        example='true',
        required=False
    )
})

# 注册模型到命名空间
ns_cmn.models[service_task_query_params.name] = service_task_query_params

# 创建对应的Parser（兼容Swagger UI）
service_task_parser = ns_cmn.parser()
service_task_parser.add_argument(
    'activityId',
    type=str,
    help='Service Task的BPMN节点ID',
    location='args'
)
service_task_parser.add_argument(
    'processDefinitionKey',
    type=str,
    help='流程定义Key',
    location='args'
)
service_task_parser.add_argument(
    'processInstanceId',
    type=str,
    help='流程实例ID',
    location='args'
)
service_task_parser.add_argument(
    'completed',
    type=str,
    choices=('true', 'false'),
    help='是否完成(true/false)',
    location='args'
)


# Marshmallow Schema（提供更强大的校验）
class ServiceTaskQuerySchema(Schema):
    activityId = fields.String(required=False)
    processDefinitionKey = fields.String(required=False)
    processInstanceId = fields.String(required=False)
    completed = fields.String(
        required=False,
        validate=validate.OneOf(['true', 'false'])
    )


@ns_cmn.route('/service-activities/filtered')
class FilteredServiceActivities(Resource):
    @ns_cmn.doc('过滤查询Service Task执行记录')
    @ns_cmn.expect(service_task_parser)  # 使用预定义的parser
    @ns_cmn.response(400, '参数错误', ns_cmn.model('Error', {
        'message': fields.String(example='参数校验失败'),
        'errors': fields.Raw(example={'activityId': 'Invalid format'})
    }))
    def get(self):
        """
        使用标准参数查询Service Task记录
        """
        try:
            args = service_task_parser.parse_args()  # 自动校验参数

            # 参数转换
            camunda_params = {
                'activityType': 'serviceTask',
                'activityId': args['activityId'],
                'processDefinitionKey': args['processDefinitionKey'],
                'processInstanceId': args['processInstanceId'],
                'finished': args['completed']
            }
            camunda_params = {k: v for k, v in camunda_params.items() if v is not None}

            response = requests.get(
                f"{CAMUNDA_URL}/history/activity-instance",
                params=camunda_params
            )
            response.raise_for_status()

            return response.json(), 200

        except ValueError as e:
            return {'message': '参数校验失败', 'errors': str(e)}, 400
        except requests.exceptions.RequestException as e:
            return {'message': f"Camunda接口错误: {str(e)}"}, 502


# 1. 完整响应模型
service_task_full_model = ns_cmn.model('ServiceTaskFull', {
    'id': fields.String(description='活动实例ID'),
    'parentActivityInstanceId': fields.String(description='父活动实例ID'),
    'activityId': fields.String(description='BPMN节点ID'),
    'activityName': fields.String(description='任务名称'),
    'activityType': fields.String(description='活动类型'),
    'processDefinitionKey': fields.String(description='流程定义Key'),
    'processDefinitionId': fields.String(description='流程定义完整ID'),
    'processInstanceId': fields.String(description='流程实例ID'),
    'executionId': fields.String(description='执行ID'),
    'taskId': fields.String(description='关联任务ID'),
    'calledProcessInstanceId': fields.String(description='调用的流程实例ID'),
    'calledCaseInstanceId': fields.String(description='调用的Case实例ID'),
    'assignee': fields.String(description='处理人'),
    'startTime': fields.DateTime(description='开始时间'),
    'endTime': fields.DateTime(description='结束时间'),
    'durationInMillis': fields.Integer(description='持续时间(毫秒)'),
    'canceled': fields.Boolean(description='是否已取消'),
    'completeScope': fields.Boolean(description='是否完成作用域'),
    'tenantId': fields.String(description='租户ID'),
    'removalTime': fields.DateTime(description='数据清除时间'),
    'rootProcessInstanceId': fields.String(description='根流程实例ID')
})

# 2. 请求体模型（空值匹配所有）
service_task_post_model = ns_cmn.model('ServiceTaskPostQuery', {
    'activityId': fields.String(
        required=False,
        description='留空或空字符串""匹配所有',
        example="Activity_0u0z4q3"
    ),
    'processDefinitionKey': fields.String(
        required=False,
        description='留空或空字符串""匹配所有',
        example="Process_1jrmxqa"
    ),
    'processInstanceId': fields.String(
        required=False,
        description='留空或空字符串""匹配所有',
        example="0353078e-6466-11f0-8678-0aecfb32578a"
    ),
    'completed': fields.String(
        required=False,
        description='留空或空字符串""匹配所有, 否则需为true/false',
        enum=['', 'true', 'false'],
        example="true"
    ),
    'minDuration': fields.Integer(
        required=False,
        description='0表示不过滤，>0时过滤(秒)',
        example=60,
        min=0
    )
})


# 3. 接口实现
@ns_cmn.route('/service-activities/full')
class FullServiceActivities(Resource):
    @ns_cmn.doc('完整Service Task查询（返回所有字段）')
    @ns_cmn.expect(service_task_post_model)
    @ns_cmn.response(200, '成功', fields.List(fields.Nested(service_task_full_model)))
    def post(self):
        """
        完整字段Service Task查询规则：
        - 空字符串/不传参：匹配所有
        - completed: ""=全部, "true"/"false"=过滤
        - minDuration: 0=不过滤, >0=过滤
        """
        try:
            # 接收参数（空body设为全空参数）
            json_data = request.get_json() or {
                'activityId': '',
                'processDefinitionKey': '',
                'processInstanceId': '',
                'completed': '',
                'minDuration': 0
            }

            # 参数校验
            class PostQuerySchema(Schema):
                activityId = marshmallow_fields.String(required=False)
                processDefinitionKey = marshmallow_fields.String(required=False)
                processInstanceId = marshmallow_fields.String(required=False)
                completed = marshmallow_fields.String(
                    required=False,
                    validate=validate.OneOf(['', 'true', 'false'])
                )
                minDuration = marshmallow_fields.Integer(
                    required=False,
                    validate=validate.Range(min=0)
                )

            args = PostQuerySchema().load(json_data)

            # 构建Camunda查询参数
            camunda_params = {'activityType': 'serviceTask'}

            # 处理completed参数
            if args.get('completed') in ['true', 'false']:
                camunda_params['finished'] = args['completed']

            # 处理其他字符串参数（非空才加入）
            for param in ['activityId', 'processDefinitionKey', 'processInstanceId']:
                if args.get(param):
                    camunda_params[param] = args[param]

            # 调用Camunda API
            response = requests.get(
                f"{CAMUNDA_URL}/history/activity-instance",
                params=camunda_params,
                timeout=10
            )
            response.raise_for_status()

            # 处理持续时间过滤
            data = response.json()
            if args.get('minDuration', 0) > 0:
                min_millis = args['minDuration'] * 1000
                data = [x for x in data if x.get('durationInMillis', 0) >= min_millis]

            # 返回原始Camunda数据（保持所有字段）
            return data, 200

        except Exception as e:
            return {'message': f"查询失败: {str(e)}"}, 500


# 0. 缓存装饰器（使用Flask-Caching）
def cache_response(timeout=300):
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            if not current_app.config.get('CACHE_ENABLED', True):
                return f(*args, **kwargs)

            cache_key = f"{request.path}?{request.query_string.decode()}"
            cached = cache.get(cache_key)
            if cached is not None:
                return cached

            result = f(*args, **kwargs)
            cache.set(cache_key, result, timeout=timeout)
            return result

        return wrapper

    return decorator


# 1. 历史任务完整响应模型
history_task_full_page_model = ns_cmn.model('HistoryTaskFullPage', {
    'id': fields.String(description='任务ID'),
    'name': fields.String(description='任务名称'),
    'assignee': fields.String(description='处理人'),
    'processInstanceId': fields.String(description='流程实例ID'),
    'processDefinitionId': fields.String(description='流程定义ID'),
    'processDefinitionKey': fields.String(description='流程定义Key'),
    'taskDefinitionKey': fields.String(description='任务定义Key'),
    'startTime': fields.DateTime(description='开始时间'),
    'endTime': fields.DateTime(description='结束时间'),
    'durationInMillis': fields.Integer(description='持续时间(毫秒)'),
    'deleteReason': fields.String(description='删除原因'),
    'priority': fields.Integer(description='优先级'),
    'tenantId': fields.String(description='租户ID'),
    'removalTime': fields.DateTime(description='数据清除时间')
})

# 2. 请求体模型（包含分页参数）
history_task_query_page_model = ns_cmn.model('HistoryTaskQueryPageParams', {
    'processDefinitionKey': fields.String(
        required=False,
        description='空字符串""=不过滤,样例:Process_1jrmxqa',
        example=""
    ),
    'finished': fields.String(
        required=False,
        description='空字符串""=不过滤 样例:true fasle 或者空',
        enum=['', 'true', 'false'],
        example=""
    ),
    'taskName': fields.String(
        required=False,
        description='空字符串""=不过滤 样例:审批',
        example=""
    ),
    'assignee': fields.String(
        required=False,
        description='空字符串""=不过滤 样例:user1',
        example=""
    ),
    'dueAfter': fields.String(
        required=False,
        description='空字符串""=不过滤，ISO格式时间 样例:2025-07-01T00:00:00+08:00',
        example=""
    ),
    'dueBefore': fields.String(
        required=False,
        description='空字符串""=不过滤，ISO格式时间 样例:2025-07-31T23:59:59+08:00',
        example=""
    ),
    'sortBy': fields.String(
        required=False,
        description='空字符串""=默认排序 样例:endTime',
        enum=['', 'endTime', 'startTime', 'duration'],
        example=""
    ),
    'sortOrder': fields.String(
        required=False,
        description='空字符串""=默认顺序 样例:desc',
        enum=['', 'asc', 'desc'],
        example=""
    ),
    'page': fields.Integer(
        required=False,
        default=1,
        min=1,
        description='页码（从1开始）'
    ),
    'pageSize': fields.Integer(
        required=False,
        default=10,
        min=1,
        max=1000,
        description='每页数量（最大1000）'
    )
})

pagination_model = ns_cmn.model('Pagination', {
    'total': fields.Integer(description='总记录数'),
    'page': fields.Integer(description='当前页码'),
    'pageSize': fields.Integer(description='每页数量'),
    'totalPages': fields.Integer(description='总页数')
})

history_task_full_page_response_model = ns_cmn.model('HistoryTaskFullPageResponse', {
    'data': fields.List(fields.Nested(history_task_full_page_model)),
    'pagination': fields.Nested(pagination_model)
})


# 3. 生产级接口实现
@ns_cmn.route('/history-page-tasks')
class HistoryTaskQueryPagePost(Resource):
    @ns_cmn.doc('分页历史任务查询(POST)')
    @ns_cmn.expect(history_task_query_page_model)
    @ns_cmn.response(200, '成功', history_task_full_page_response_model)
    @cache_response(timeout=300)  # 缓存5分钟
    def post(self):
        """
        历史任务查询（生产级实现）：
        - 支持空参数匹配全部
        - 内置分页和缓存
        - 自动性能优化
        """
        try:
            # 接收并校验参数
            json_data = request.get_json() or {
                'processDefinitionKey': '',
                'finished': '',
                'taskName': '',
                'assignee': '',
                'dueAfter': '',
                'dueBefore': '',
                'sortBy': '',
                'sortOrder': '',
                'page': 1,
                'pageSize': 50
            }

            class QuerySchema(Schema):
                processDefinitionKey = marshmallow_fields.String(required=False)
                finished = marshmallow_fields.String(
                    required=False,
                    validate=validate.OneOf(['', 'true', 'false'])
                )
                taskName = marshmallow_fields.String(required=False)
                assignee = marshmallow_fields.String(required=False)
                dueAfter = marshmallow_fields.String(required=False)
                dueBefore = marshmallow_fields.String(required=False)
                sortBy = marshmallow_fields.String(
                    required=False,
                    validate=validate.OneOf(['', 'endTime', 'startTime', 'duration'])
                )
                sortOrder = marshmallow_fields.String(
                    required=False,
                    validate=validate.OneOf(['', 'asc', 'desc'])
                )
                page = marshmallow_fields.Integer(
                    required=False,
                    validate=validate.Range(min=1),
                    load_default=1
                )
                pageSize = marshmallow_fields.Integer(
                    required=False,
                    validate=validate.Range(min=1, max=1000),
                    load_default=50
                )

            args = QuerySchema().load(json_data)

            # 构建基础查询参数（性能优化：只查询必要字段）
            camunda_params = {
                'deserializeValues': 'false',  # 不反序列化变量值
                'maxResults': args['pageSize'],
                'firstResult': (args['page'] - 1) * args['pageSize']
            }

            # 添加过滤条件（非空字符串才添加）
            if args.get('processDefinitionKey', ''):
                camunda_params['processDefinitionKey'] = args['processDefinitionKey']
            if args.get('finished', '') in ['true', 'false']:
                camunda_params['finished'] = args['finished']
            if args.get('taskName', ''):
                camunda_params['taskName'] = args['taskName']
            if args.get('assignee', ''):
                camunda_params['assignee'] = args['assignee']

            # 处理时间参数
            if args.get('dueAfter', ''):
                try:
                    datetime.fromisoformat(args['dueAfter'])
                    camunda_params['dueAfter'] = args['dueAfter']
                except ValueError:
                    raise BadRequest("dueAfter必须是有效的ISO时间格式")

            if args.get('dueBefore', ''):
                try:
                    datetime.fromisoformat(args['dueBefore'])
                    camunda_params['dueBefore'] = args['dueBefore']
                except ValueError:
                    raise BadRequest("dueBefore必须是有效的ISO时间格式")

            # 处理排序
            if args.get('sortBy', ''):
                camunda_params['sortBy'] = args['sortBy']
            if args.get('sortOrder', ''):
                camunda_params['sortOrder'] = args['sortOrder']

            # 先查询总数（性能优化：只有非第一页或需要总页数时才查询）
            total = -1  # -1表示未获取总数
            if args['page'] == 1 or current_app.config.get('ALWAYS_GET_TOTAL', False):
                count_params = {k: v for k, v in camunda_params.items()
                                if k not in ['maxResults', 'firstResult']}
                count_response = requests.get(
                    f"{CAMUNDA_URL}/history/task/count",
                    params=count_params,
                    timeout=5
                )
                count_response.raise_for_status()
                total = count_response.json()['count']

            # 查询数据
            data_response = requests.get(
                f"{CAMUNDA_URL}/history/task",
                params=camunda_params,
                timeout=10
            )
            data_response.raise_for_status()
            data = data_response.json()

            # 如果是第一页且之前没查总数，用当前返回数量估算
            if total == -1 and args['page'] == 1:
                total = len(data) if len(data) < args['pageSize'] else args['pageSize'] * 10

            return {
                'data': data,
                'pagination': {
                    'total': total,
                    'page': args['page'],
                    'pageSize': args['pageSize'],
                    'totalPages': max(1, (total + args['pageSize'] - 1) // args['pageSize'])
                }
            }, 200

        except BadRequest as e:
            return {'message': str(e)}, 400
        except requests.exceptions.RequestException as e:
            current_app.logger.error(f"Camunda接口错误: {str(e)}")
            return {'message': "服务暂时不可用"}, 502
        except Exception as e:
            current_app.logger.exception("服务器内部错误")
            return {'message': "服务器处理失败"}, 500


# 1. 缓存测试响应模型
cache_test_model = ns_cmn.model('CacheTestResponse', {
    'status': fields.String(description='测试状态'),
    'cache_content': fields.Raw(description='缓存内部存储内容'),
    'retrieved_value': fields.String(description='获取的缓存值')
})


class CacheTester:
    """缓存测试工具类"""

    @staticmethod
    def cache_response(timeout=300):
        """缓存装饰器（与原有保持一致）"""

        def decorator(f):
            @wraps(f)
            def wrapper(*args, **kwargs):
                if not current_app.config.get('CACHE_ENABLED', True):
                    return f(*args, **kwargs)

                cache_key = f"{request.path}?{request.query_string.decode()}"
                cached = cache.get(cache_key)
                if cached is not None:
                    return cached

                result = f(*args, **kwargs)
                cache.set(cache_key, result, timeout=timeout)
                return result

            return wrapper

        return decorator

    @classmethod
    def test_cache_operation(cls):
        """执行缓存测试操作"""
        cache.set('test_key', 'test_value')

        result = {
            'status': 'success',
            'retrieved_value': cache.get('test_key')
        }

        if hasattr(cache.cache, '_cache'):
            result['cache_content'] = cache.cache._cache
        else:
            result['status'] = 'warning'
            result['message'] = '无法访问内存缓存存储'

        return result


# 2. 缓存测试接口
@ns_cmn.route('/test-cache')
class CacheTestResource(Resource):
    @ns_cmn.doc('测试缓存功能')
    @ns_cmn.response(200, '成功', cache_test_model)
    @ns_cmn.response(500, '服务器错误')
    @CacheTester.cache_response(timeout=60)  # 使用类中的装饰器
    def get(self):
        """
        缓存功能测试接口
        - 测试缓存设置/获取功能
        - 返回缓存内部存储状态（仅SimpleCache可见）
        """
        try:
            test_result = CacheTester.test_cache_operation()
            return test_result, 200
        except Exception as e:
            current_app.logger.error(f"缓存测试失败: {str(e)}")
            return {
                'status': 'error',
                'message': '缓存测试失败'
            }, 500


# 1. 定义所有任务类型的枚举模型
task_type_all_model = ns_cmn.model('TaskType', {
    'task_type': fields.String(
        required=True,
        description='任务类型',
        enum=['task', 'external-task', 'job',
              'history/task', 'history/external-task-log',
              'history/job-log', 'incident'],
        example="task"
    ),
    'task_id': fields.String(
        required=True,
        description='任务/作业/事件ID',
        example="c2f5f792-6486-11f0-8678-0aecfb32578a"
    )
})

# 2. 端点映射配置
TASK_ENDPOINTS = {
    'task': '/task/',
    'external-task': '/external-task/',
    'job': '/job/',
    'history/task': '/history/task/',
    'history/external-task-log': '/history/external-task-log/',
    'history/job-log': '/history/job-log/',
    'incident': '/incident/'
}


# 3. 实现统一查询
@ns_cmn.route('/unified-query')
class UnifiedTaskQuery(Resource):
    @ns_cmn.doc('全类型任务精确查询')
    @ns_cmn.expect(task_type_all_model)
    @ns_cmn.response(200, '成功', task_model)
    def post(self):
        """
        支持查询所有Camunda任务类型：
        - 精确指定类型时直接查询
        - 自动重试机制（当类型不匹配时）
        """
        data = request.get_json()
        task_id = data['task_id']
        task_type = data['task_type']

        # 第一步：尝试指定类型的查询
        response = self._query_camunda(task_id, task_type)
        if response:
            return {"type": task_type, "data": response}, 200

        # 第二步：自动类型探测（如果指定类型未找到）
        for fallback_type in ['task', 'external-task', 'job']:
            if fallback_type != task_type:
                response = self._query_camunda(task_id, fallback_type)
                if response:
                    return {
                        "type": fallback_type,
                        "original_type": task_type,
                        "data": response
                    }, 200

        ns_cmn.abort(404, "未找到匹配的任务数据")

    def _query_camunda(self, task_id, task_type):
        """封装Camunda查询逻辑"""
        try:
            url = f"{CAMUNDA_URL}{TASK_ENDPOINTS[task_type]}{task_id}"
            response = requests.get(url, timeout=3)
            return response.json() if response.status_code == 200 else None
        except:
            return None
